/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "napi_init.h"
#include "napi_utils.h"
#include "message_types.h"
#include "messages.h"
#include "Node.h"
#include "ohmw_framework/bincode/Encoder.h"
#include "ohmw_framework/bincode/Decoder.h"
#include <cstdint>
#include <napi/native_api.h>
#include <memory>
#include <map>
#include <functional>
#include <stdint.h>
#include <string>
#include <uv.h>

using namespace OHOS::OHMW;

struct NodeWrap {
    Node::Type type;
    std::shared_ptr<Node> node;
    napi_env env;
    napi_ref object_ref;                                  // 存Node对象
    std::map<std::string, napi_ref> onRecvCallbackRef;    // 存onRecv的回调函数
    std::map<std::string, napi_ref> onRecvMsgCallbackRef; // 存onRecvMsg的回调函数
};

std::map<std::string, std::shared_ptr<struct NodeWrap>> g_node_wraps;

void FinalizeNode(napi_env env, void *data, void *hint) {
    struct NodeWrap *nodeWrap = static_cast<struct NodeWrap *>(data);
    for (auto it = g_node_wraps.begin(); it != g_node_wraps.end(); it++) {
        if (it->second.get() == nodeWrap) {
            napi_delete_reference(env, nodeWrap->object_ref);
            for (auto it2 = nodeWrap->onRecvCallbackRef.begin(); it2 != nodeWrap->onRecvCallbackRef.end(); it2++) {
                napi_delete_reference(env, it2->second);
            }
            nodeWrap->node.reset();
            g_node_wraps.erase(it);
            break;
        }
    }
}

// 构造函数
napi_value NodeConstructor(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(2);

    NAPI_CHECKOUT_STRING(nodePath, 1024, args[0]);

    int32_t type;
    napi_status _status = napi_get_value_int32(env, args[1], &type);
    NAPI_CHECK_STATUS("NodeConstructor napi_get_value_int32 failed");
    Node::Type nodeType = static_cast<Node::Type>(type);
    if (nodeType < Node::Type::DORA || nodeType > Node::Type::ROS2) {
        CLOGE("Invalid server type");
        return nullptr;
    }

    if (g_node_wraps.find(nodePath) != g_node_wraps.end()) {
        std::shared_ptr<struct NodeWrap> nodeWrap = g_node_wraps[nodePath];
        // if (nodeWrap->type == nodeType) { // 找到已经创建的节点直接返回
        //     return nullptr;
        // } else { // 找到已经创建的节点但是类型不匹配，报错
        //     CLOGE("Node already exists with different type");
        //     return nullptr;
        // }
        napi_value returnValue;
        _status = napi_get_reference_value(env, nodeWrap->object_ref, &returnValue);
        NAPI_CHECK_STATUS("NodeConstructor napi_get_reference_value failed");
        CLOGI("NodeConstructor found existing node");
        return returnValue;
    } else { // 节点还没创建，创建节点
        std::shared_ptr<struct NodeWrap> nodeWrap = std::make_shared<struct NodeWrap>();
        nodeWrap->type = nodeType;
        nodeWrap->node = std::make_shared<Node>(nodePath, nodeType);
        _status = napi_wrap(env, thisVar, reinterpret_cast<void *>(nodeWrap.get()), FinalizeNode, nullptr, nullptr);
        NAPI_CHECK_STATUS("NodeConstructor napi_wrap failed");
        _status = napi_create_reference(env, thisVar, 1, &nodeWrap->object_ref);
        NAPI_CHECK_STATUS("NodeConstructor napi_create_reference failed");
        nodeWrap->env = env;
        g_node_wraps[nodePath] = nodeWrap;
        CLOGI("NodeConstructor created new node");
        return thisVar;
    }
}

napi_value NodeSend(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(2);

    NAPI_CHECKOUT_STRING(path, 1024, args[0]);
    NAPI_CHECKOUT_UINT8_ARRAY(data, len, args[1]);

    struct NodeWrap *nodeWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nodeWrap));

    CLOGI("calling node->Send(%{public}s,%{public}d,%{public}zu)", path.c_str(), data[0], len);
    int32_t result = nodeWrap->node->Send(path, data, len);

    NAPI_PACKAGE_BOOL(ret, result == 0);
    return ret;
}

napi_value NodeRecv(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(2);

    NAPI_CHECKOUT_STRING(path, 1024, args[0]);
    NAPI_CHECKOUT_UINT32(timeout, args[1]);

    struct NodeWrap *nodeWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nodeWrap));

    uint8_t data[1024];
    uint32_t len = 1024;
    int32_t result = nodeWrap->node->Recv(path, data, &len, timeout);
    CLOGI("calling node->Recv(%{public}d,%{public}d)", data[0], len);
    if (result == 0) {
        NAPI_PACKAGE_UINT8_ARRAY(ret, data, len);
        return ret;
    } else {
        NAPI_PACKAGE_UNDEFINED(ret);
        return ret;
    }
}

struct OnRecvCallbackWorkData {
    struct NodeWrap *nodeWrap;
    std::string path;
    napi_ref callbackRef;
};
napi_value onrecv_callback_work(struct OnRecvCallbackWorkData *wd) {
    struct NodeWrap *nodeWrap = wd->nodeWrap;
    std::string path = wd->path;
    napi_value callbackData;
    napi_get_reference_value(nodeWrap->env, wd->callbackRef, &callbackData);

    napi_env env = nodeWrap->env;

    napi_value callbackFunc;
    napi_status _status = napi_get_reference_value(env, nodeWrap->onRecvCallbackRef[path], &callbackFunc);
    NAPI_CHECK_STATUS("NodeOnRecv napi_get_reference_value failed");

    napi_value argv[1] = {callbackData};
    napi_value result;
    _status = napi_call_function(env, nullptr, callbackFunc, 1, argv, &result);
    NAPI_CHECK_STATUS("NodeOnRecv napi_call_function failed");
    CLOGI("NodeOnRecv called callback");
    return nullptr;
}
int32_t create_onrecv_work(struct NodeWrap *nodeWrap, std::string path, uint8_t *data, uint32_t len) {
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(nodeWrap->env, &loop);
    if (loop == nullptr) {
        CLOGE("DmNapiInitCallback: OnRemoteDied, No loop");
        return -1;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        CLOGE("DmNapiInitCallback: OnRemoteDied, No memory");
        return -1;
    }
    struct OnRecvCallbackWorkData *wd = new (std::nothrow) struct OnRecvCallbackWorkData;
    wd->nodeWrap = nodeWrap;
    wd->path = path;
    napi_env env = nodeWrap->env;
    NAPI_PACKAGE_UINT8_ARRAY(callbackData, data, len);
    napi_create_reference(env, callbackData, 1, &wd->callbackRef);
    work->data = wd;
    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int _status) {
            struct OnRecvCallbackWorkData *wd = static_cast<struct OnRecvCallbackWorkData *>(work->data);
            onrecv_callback_work(wd);
            napi_delete_reference(wd->nodeWrap->env, wd->callbackRef);
            delete wd;
            delete work;
        });
    return 0;
}
napi_value NodeOnRecv(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(2);

    NAPI_CHECKOUT_STRING(path, 1024, args[0]);
    NAPI_CHECKOUT_FUNCTION(callback, args[1]);

    struct NodeWrap *nodeWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nodeWrap));

    if (nodeWrap->onRecvCallbackRef.find(path) != nodeWrap->onRecvCallbackRef.end()) {
        napi_delete_reference(env, nodeWrap->onRecvCallbackRef[path]);
        nodeWrap->onRecvCallbackRef.erase(path);
    }
    napi_status _status = napi_create_reference(env, callback, 1, &nodeWrap->onRecvCallbackRef[path]);
    NAPI_CHECK_STATUS("NodeOnRecv napi_create_reference failed");

    int32_t result = nodeWrap->node->OnRecv(
        path, [nodeWrap, path](uint8_t *data, uint32_t len) { create_onrecv_work(nodeWrap, path, data, len); });

    NAPI_PACKAGE_BOOL(ret, result == 0);
    return ret;
}

napi_value NodeSendMsg(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(2);

    NAPI_CHECKOUT_STRING(path, 1024, args[0]);
    NAPI_GET_PROPERTY(args[1], "__type", napiType);
    NAPI_CHECKOUT_STRING(type, 4096, napiType);

#define SEND_MSG_HANDLER(MSG_TYPE, CXX_TYPE) \
    {MSG_TYPE, [](napi_env env, napi_callback_info info, napi_value thisVar, napi_value* args, const std::string& path) -> napi_value { \
        CXX_TYPE *obj; \
        napi_unwrap(env, args[1], reinterpret_cast<void **>(&obj)); \
        struct NodeWrap *nodeWrap; \
        napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nodeWrap)); \
        \
        DefaultEncoder encoder; \
        std::vector<uint8_t> buffer; \
        obj->Encode(encoder, buffer, env); \
        \
        int32_t result = nodeWrap->node->Send(path, buffer.data(), buffer.size()); \
        NAPI_PACKAGE_BOOL(ret, result == 0); \
        return ret; \
    }},

    // 使用宏简化映射表定义
    static const std::map<std::string, std::function<napi_value(napi_env, napi_callback_info, napi_value, napi_value*, const std::string&)>> sendMsgHandlers = {
        DO_WITH_MESSAGE_LIST(SEND_MSG_HANDLER)
    };

    CLOGI("NodeSendMsg path: %{public}s, type: %{public}s", path.c_str(), type.c_str());
    auto handler = sendMsgHandlers.find(type);
    if (handler != sendMsgHandlers.end()) {
        return handler->second(env, info, thisVar, args, path);
    }

    bool result = true;
    NAPI_PACKAGE_BOOL(ret, result);
    return ret;
}

napi_value NodeRecvMsg(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(3);

    NAPI_CHECKOUT_STRING(path, 1024, args[0]);
    NAPI_CHECKOUT_STRING(type, 1024, args[1]);
    NAPI_CHECKOUT_UINT32(timeout, args[2]);

    struct NodeWrap *nodeWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nodeWrap));

#define RECV_MSG_HANDLER(MSG_TYPE, CXX_TYPE) \
    {MSG_TYPE, [](napi_env env, struct NodeWrap* nodeWrap, const std::string& path, uint32_t timeout) -> napi_value { \
        CXX_TYPE *msg; \
        napi_value napiMsg = CXX_TYPE::NapiInstance(env); \
        napi_unwrap(env, napiMsg, reinterpret_cast<void **>(&msg)); \
        int32_t result = nodeWrap->node->RecvMsg(path, *msg, timeout); \
        CLOGI("NodeRecvMsg result: %{public}d", result); \
        if (result == 0) { \
            return napiMsg; \
        } else { \
            NAPI_PACKAGE_UNDEFINED(ret); \
            return ret; \
        } \
    }},

    // 使用宏简化映射表定义
    static const std::map<std::string, std::function<napi_value(napi_env, struct NodeWrap*, const std::string&, uint32_t)>> recvMsgHandlers = {
        // DO_WITH_MESSAGE_LIST(RECV_MSG_HANDLER)
    };

    auto handler = recvMsgHandlers.find(type);
    if (handler != recvMsgHandlers.end()) {
        return handler->second(env, nodeWrap, path, timeout);
    }

    // 如果所有类型都接收失败，返回 null
    CLOGI("NodeRecvMsg failed to receive any message type");
    napi_value nullValue;
    napi_get_null(env, &nullValue);
    return nullValue;
}

template <typename T> void create_onrecv_msg_work(struct NodeWrap *nodeWrap, std::string path, T &msg) {
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(nodeWrap->env, &loop);
    if (loop == nullptr) {
        CLOGE("DmNapiInitCallback: OnRemoteDied, No loop");
        return;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        CLOGE("DmNapiInitCallback: OnRemoteDied, No memory");
        return;
    }
    struct OnRecvCallbackWorkData *wd = new (std::nothrow) struct OnRecvCallbackWorkData;
    wd->nodeWrap = nodeWrap;
    wd->path = path;
    napi_env env = nodeWrap->env;
    napi_value callbackData;
    // 通过msg->Decode()构建要返回的napi_value
    napi_create_reference(env, callbackData, 1, &wd->callbackRef);
    work->data = wd;
    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int _status) {
            struct OnRecvCallbackWorkData *wd = static_cast<struct OnRecvCallbackWorkData *>(work->data);
            onrecv_callback_work(wd);
            napi_delete_reference(wd->nodeWrap->env, wd->callbackRef);
            delete wd;
            delete work;
        });
}

napi_value NodeOnRecvMsg(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(3);

    NAPI_CHECKOUT_STRING(path, 1024, args[0]);
    NAPI_CHECKOUT_STRING(type, 1024, args[1]);
    NAPI_CHECKOUT_FUNCTION(callback, args[2]);

    struct NodeWrap *nodeWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nodeWrap));

    if (nodeWrap->onRecvMsgCallbackRef.find(path) != nodeWrap->onRecvMsgCallbackRef.end()) {
        napi_delete_reference(env, nodeWrap->onRecvMsgCallbackRef[path]);
        nodeWrap->onRecvMsgCallbackRef.erase(path);
    }
    napi_status _status = napi_create_reference(env, callback, 1, &nodeWrap->onRecvMsgCallbackRef[path]);
    NAPI_CHECK_STATUS("NodeOnRecvMsg napi_create_reference failed");

#define ON_RECV_MSG_HANDLER(MSG_TYPE, CXX_TYPE) \
    {MSG_TYPE, [](struct NodeWrap* nodeWrap, const std::string& path) -> int32_t { \
        return nodeWrap->node->OnRecv( \
            path, [nodeWrap, path](uint8_t *data, uint32_t len) { \
                CXX_TYPE msg; \
                DefaultDecoder decoder(sizeof(uint32_t)); \
                bool ret = msg.Decode(decoder, std::vector<uint8_t>(data, data + len)); \
                if (ret) { \
                    create_onrecv_msg_work(nodeWrap, path, msg); \
                } \
            }); \
    }},

    // 使用宏简化映射表定义
    static const std::map<std::string, std::function<int32_t(struct NodeWrap*, const std::string&)>> msgHandlers = {
        DO_WITH_MESSAGE_LIST(ON_RECV_MSG_HANDLER)
    };

    // 根据消息类型调用相应的OnRecvMsg
    int32_t result = -1;
    auto handler = msgHandlers.find(type);
    if (handler != msgHandlers.end()) {
        result = handler->second(nodeWrap, path);
    }

    NAPI_PACKAGE_BOOL(ret, result == 0);
    return ret;
}

// 清理宏定义
#undef SEND_MSG_HANDLER
#undef RECV_MSG_HANDLER
#undef ON_RECV_MSG_HANDLER

napi_value OhmwNapiNodeInit(napi_env env, napi_value exports) {
    {
        napi_property_descriptor properties[] = {
            {"send", nullptr, NodeSend, nullptr, nullptr, nullptr, napi_default, nullptr},
            {"recv", nullptr, NodeRecv, nullptr, nullptr, nullptr, napi_default, nullptr},
            {"onRecv", nullptr, NodeOnRecv, nullptr, nullptr, nullptr, napi_default, nullptr},

            {"sendMsg", nullptr, NodeSendMsg, nullptr, nullptr, nullptr, napi_default, nullptr},
            {"recvMsg", nullptr, NodeRecvMsg, nullptr, nullptr, nullptr, napi_default, nullptr},
            {"onRecvMsg", nullptr, NodeOnRecvMsg, nullptr, nullptr, nullptr, napi_default, nullptr}};

        napi_value cons;
        napi_define_class(env, "Node", NAPI_AUTO_LENGTH, NodeConstructor, nullptr,
            sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons);
        napi_set_named_property(env, exports, "Node", cons);
    }
    {
        napi_value enumObj;
        napi_create_object(env, &enumObj);
        napi_value dora, ros, ros2;
        napi_create_int32(env, static_cast<int32_t>(Node::Type::DORA), &dora);
        napi_create_int32(env, static_cast<int32_t>(Node::Type::ROS), &ros);
        napi_create_int32(env, static_cast<int32_t>(Node::Type::ROS2), &ros2);
        napi_set_named_property(env, enumObj, "DORA", dora);
        napi_set_named_property(env, enumObj, "ROS", ros);
        napi_set_named_property(env, enumObj, "ROS2", ros2);
        napi_set_named_property(env, exports, "NodeType", enumObj);
    }
    return exports;
}
